﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Drawing;

namespace MonoStrategy
{
    [Serializable]
    public class AnimationFrame
    {
        private Animation m_AnimationOrNull;
        internal byte[] m_Bitmap;
        private int m_OffsetX;
        private int m_OffsetY;
        private int m_Width;
        private int m_Height;

        public Int32 GFXFileChecksum { get; set; }
        public Int32 GFXFrame { get; set; }
        public Int32 GFXSequence { get; set; }
        public Int32 OriginalOffsetX { get; set; } // not saved
        public Int32 OriginalOffsetY { get; set; } // not saved
        public Int64 Checksum { get; internal set; }
        public int OffsetX { get { return m_OffsetX; } set { ForceWriteable(); m_OffsetX = value; } }
        public int OffsetY { get { return m_OffsetY; } set { ForceWriteable(); m_OffsetY = value; } }
        public int Width { get { return m_Width; } set { ForceWriteable(); m_Width = value; } }
        public int Height { get { return m_Height; } set { ForceWriteable(); m_Height = value; } }
        public int Index { get; internal set; }

        public Animation AnimationOrNull { get { return m_AnimationOrNull; } }

        internal void Save(BinaryWriter inWriter)
        {
            // write frame to stream
            inWriter.Write((Byte)5); // frame type ID
            inWriter.Write((UInt16)0x1000); // frame version

            inWriter.Write((Int32)Width);
            inWriter.Write((Int64)Checksum);
            inWriter.Write((Int32)OffsetX);
            inWriter.Write((Int32)OffsetY);
            inWriter.Write((Int32)GFXFileChecksum);
            inWriter.Write((Int32)GFXFrame);
            inWriter.Write((Int32)GFXSequence);
            inWriter.Write((Int32)Height);
        }

        internal static AnimationFrame Load(Animation inAnim, BinaryReader inReader)
        {
            AnimationFrame result = new AnimationFrame(inAnim);

            if (inReader.ReadByte() != 5)
                throw new InvalidDataException();

            switch (inReader.ReadUInt16())
            {
                case 0x1000:
                    {
                        result.Width = inReader.ReadInt32();
                        result.Checksum = inReader.ReadInt64();
                        result.OffsetX = inReader.ReadInt32();
                        result.OffsetY = inReader.ReadInt32();
                        result.GFXFileChecksum = inReader.ReadInt32();
                        result.GFXFrame = inReader.ReadInt32();
                        result.GFXSequence = inReader.ReadInt32();
                        result.Height = inReader.ReadInt32();
                    } break;

                default:
                    throw new InvalidDataException();
            }

            return result;
        }

        public byte[] ToArray()
        {
            if (Source == null)
                return null;

            return (byte[])m_Bitmap.Clone();
        }

        internal AnimationFrame(Animation inParent)
        {
            m_AnimationOrNull = inParent;
            OriginalOffsetX = OriginalOffsetY = Int32.MinValue;
        }

        [NonSerialized]
        private Bitmap m_Source;


        public void SetBitmap(System.Drawing.Bitmap inBitmap)
        {
            ForceWriteable();

            MemoryStream stream = new MemoryStream();

            inBitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);

            SetBitmap(stream.ToArray());
        }

        public void SetBitmap(byte[] inSource)
        {
            ForceWriteable();

            try
            {
                m_Bitmap = (byte[])inSource.Clone();
                m_Source = (Bitmap)Bitmap.FromStream(new MemoryStream(m_Bitmap));
                byte[] hash = System.Security.Cryptography.MD5.Create().ComputeHash(new MemoryStream(inSource));

                Checksum = 0;

                for (int i = 0; i < 8; i++)
                {
                    Checksum |= (((Int64)hash[i]) << (i * 8));
                }
            }
            catch
            {
                m_Bitmap = null;
                m_Source = null;
                Checksum = 0;
            }
        }

        public Bitmap Source
        {
            get
            {

                if (m_Bitmap == null)
                {
                    if(AnimationOrNull == null)
                        return null;

                    if (AnimationOrNull.SetOrNull != null)
                    {
                        AnimationClass animClass = AnimationOrNull.SetOrNull.Class;

                        m_Bitmap = animClass.LoadFrame(this);
                    }
                    else
                        m_Bitmap = AnimationOrNull.Library.LoadFrame(this);

                    // m_Bitmap is now set, otherwise an exception is thrown by LoadFrame().
                }


                if (m_Source == null)
                    m_Source = m_Source = (Bitmap)Bitmap.FromStream(new MemoryStream(m_Bitmap));

                return m_Source;
            }
        }

        public void Clone(AnimationFrame outClone)
        {
            outClone.SetBitmap(Source);
            outClone.Height = Height;
            outClone.GFXFileChecksum = GFXFileChecksum;
            outClone.GFXFrame = GFXFrame;
            outClone.GFXSequence = GFXSequence;
            outClone.OffsetX = OffsetX;
            outClone.OffsetY = OffsetY;
            outClone.Width = Width;
        }

        private void ForceWriteable()
        {
            if(AnimationOrNull != null)
                AnimationOrNull.Library.ForceWriteable();
        }
    }
}
